﻿namespace MPNet6.Server;
public static class MPServer
{
    public const int SendBufferSize = 64 * 1024;
    const int ReceiveBufferSize = 64 * 1024;

    private static readonly ConcurrentDictionary<string, MPSession> _MPSessions = new();

    private static TcpListener _TcpListener = default!;
    private static CancellationTokenSource _StopTokenSource = default!;
    private static CancellationToken _StopToken = default!;
    private static int _RegisteringClientCount = 0;

    public static ILogger Logger = default!;
    public static List<KeyValuePair<string, string>> GetDetail()
    {

        var items = _MPSessions.Select(ii => new KeyValuePair<string, string>(ii.Key, ii.Value.ClientInfo)).OrderBy(ii => ii.Key).ToList();
        return items;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="port">端口号</param>
    /// <param name="msgs">上次退出时保存的信息</param>
    /// <param name="logger"></param>
    public static void Start(int port, List<MPClientMessage>? msgs, ILogger logger)
    {
        Logger = logger;
        if (msgs != null)
            msgs.ForEach(ii => _MPSessions[ii.ClientId] = new MPSession(ii.Msgs));

        _TcpListener = new TcpListener(IPAddress.Any, port);
        _TcpListener.Start();
        _StopTokenSource = new CancellationTokenSource();
        _StopToken = _StopTokenSource.Token;
        _ = Task.Run(async () => await AcceptClientConnectionsAsync());
    }

    private static async Task AcceptClientConnectionsAsync()
    {
        while (!_StopToken.IsCancellationRequested)
        {
            try
            {
                var tcpClient = await _TcpListener.AcceptTcpClientAsync(_StopToken).ConfigureAwait(false);
                if (tcpClient == null)
                    continue;
                tcpClient.LingerState = new LingerOption(true, 2);
                tcpClient.ReceiveBufferSize = ReceiveBufferSize;
                tcpClient.SendBufferSize = SendBufferSize;
                Register(tcpClient);
            }
            catch (OperationCanceledException)//stopTokenSource.Cancel()
            {
                break;
            }
            catch
            {
                await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
            }
        }
    }
    private static void Register(TcpClient tcpClient)
    {
        _ = Task.Run(() =>
        {
            Interlocked.Increment(ref _RegisteringClientCount);
            var buffer = new byte[1024];
            var stream = tcpClient.GetStream();
            stream.ReadTimeout = 2000;

            try
            {
                var len = stream.Read(buffer);
                if (len <= 0) //客户端关闭(TcpClient.Close或客户端程序退出)
                {
                    tcpClient.Close();
                    stream.Close();
                    return;
                }
                switch (buffer[0])
                {
                    case 0://More Connect
                    case 1://More Reconnect
                        {
                            var clientId = Encoding.UTF8.GetString(buffer, 1, len - 1);
                            Logger.LogInformation($"More Connect:{clientId}");
                            _MPSessions.GetOrAdd(clientId, new MPSession()).ConnectMore(tcpClient, stream, buffer[0]);
                            break;
                        }
                    case 2://Once Connect
                        {
                            var clientId = Encoding.UTF8.GetString(buffer, 1, len - 1);
                            Logger.LogInformation($"Once Connect:{clientId}");
                            _MPSessions.GetOrAdd(clientId, new MPSession()).ConnectOnce(tcpClient, stream);
                            break;
                        }
                    case 3://Once Reconnect
                        {
                            var clientId = Encoding.UTF8.GetString(buffer, 1, len - 1);
                            Logger.LogInformation($"Once Reconnect:{clientId}");
                            _MPSessions.GetOrAdd(clientId, new MPSession()).ReconnectOnce(tcpClient, stream);
                            break;
                        }
                    case 4://SendMessage
                        {
                            stream.Write(new byte[] { 0 });
                            var msgs = Encoding.UTF8.GetString(buffer, 1, len - 1).Split('\t');
                            Logger.LogInformation($"SendMessage:{msgs[0]}");
                            SendMessage(msgs[0], msgs[1]);
                            break;

                        }

                }
            }
            catch (Exception ex)
            {
                Logger.LogError("ReadError:{ex}", ex);
                tcpClient.Close();
                stream.Close();
            }
            finally
            {
                Interlocked.Decrement(ref _RegisteringClientCount);
            }
        });
    }

    public static void SendMessage(string clientId, string msg)
    {
        if (msg.Length > SendBufferSize)
            throw new Exception($"Message can't more than {SendBufferSize}bytes");

        Task.Run(() => _MPSessions.GetOrAdd(clientId, new MPSession()).SendMessage(msg));
    }
    public static void Kill(string clientId) => _MPSessions.TryRemove(clientId, out _);

    public static List<MPClientMessage> Stop()
    {
        _StopTokenSource?.Cancel();
        _TcpListener.Stop();

        List<MPClientMessage> items = new();
        foreach (var kv in _MPSessions)
        {
            var msgs = kv.Value.Stop();
            if (!string.IsNullOrEmpty(msgs))
                items.Add(new MPClientMessage(kv.Key, msgs));
        }
        while (_RegisteringClientCount > 0)
            Thread.Sleep(500);
        return items;
    }

}